
#include "cvf_ImageTransform.h"

// [*] CDenseSingleDimensionIntegralImage

CDenseSingleDimensionIntegralImage::CDenseSingleDimensionIntegralImage()
{
	ClearContent();
}

void CDenseSingleDimensionIntegralImage::ClearContent()
{
	W = H = 0;
	_W1 = _H1 = 1;
	_W2 = _H2 = 1;
	WH = 0; _W1H1 = 1; _W2H2 = 4;
	_imgEnlarged.assign(2, 2, 2, 2); _imgEnlarged.fill(0.0);
	_InitIntegralFunctions();
}

size_t CDenseSingleDimensionIntegralImage::GetMemoryUsage() const
{
	return 0
		+ sizeof(W) + sizeof(H) + sizeof(WH)
		+ sizeof(_W1) + sizeof(_H1) + sizeof(_W1H1)
		+ sizeof(_W2) + sizeof(_H2) + sizeof(_W2H2)
		+ CExternalTools::GetMemoryUsage(_imgEnlarged)
		//+ CExternalTools::GetMemoryUsage(_img_Ic)
		+ CExternalTools::GetMemoryUsage(_img_Vx)
		+ CExternalTools::GetMemoryUsage(_img_Hy)
		//+ CExternalTools::GetMemoryUsage(_img_Ix)
		//+ CExternalTools::GetMemoryUsage(_img_Iy)
		+ CExternalTools::GetMemoryUsage(_img_Sum);
}

void CDenseSingleDimensionIntegralImage::_InitIntegralFunctions()
{
	int ix, iy;
	double* pd;
	const double* qp;
	const double* qa;
	const double* qb;
	const double* qc;
	const double* qd;

	_base_enlarged = _imgEnlarged.data;

	_img_Sum.assign(_W1, _H1, 1, 1); _img_Sum.fill(0.0);
	for ( iy = 1; iy < _H1; ++iy )
	{
		qa = _imgEnlarged.data + 0 + (iy - 1) * _W2;
		qb = qa + 1;
		qc = qa + _W2;
		qd = qa + 1 + _W2;
		pd = _img_Sum.data + 1 + iy * _W1;
		qp = pd - 1;
		for ( ix = 1; ix < _W1; ++ix )
			*(pd++) = *(qp++) + *(qa++) + *(qb++) + *(qc++) + *(qd++);
		pd = _img_Sum.data + 1 + iy * _W1;
		qp = pd - _W1;
		for ( ix = 1; ix < _W1; ++ix )
			*(pd++) += *(qp++);
	}
	_base_x0y0 = _img_Sum.data + 0 * _W1H1;

	_img_Vx.assign(_W2, _H1, 1, 1); _img_Vx.fill(0.0);
	for ( iy = 1; iy < _H1; ++iy )
	{
		qa = _imgEnlarged.data + 0 + (iy - 1) * _W2;
		qb = qa + _W2;
		pd = _img_Vx.data + iy * _W2;
		qp = pd - _W2;
		for ( ix = 0; ix < _W2; ++ix )
			*(pd++) = *(qp++) + *(qa++) + *(qb++);
	}
	_base_Vx = _img_Vx.data;

	_img_Hy.assign(_W1, _H2, 1, 1); _img_Hy.fill(0.0);
	for ( iy = 0; iy < _H2; ++iy )
	{
		qa = _imgEnlarged.data + 0 + iy * _W2;
		qb = qa + 1;
		pd = _img_Hy.data + 1 + iy * _W1;
		qp = pd - 1;
		for ( ix = 1; ix < _W1; ++ix )
			*(pd++) = *(qp++) + *(qa++) + *(qb++);
	}
	_base_Hy = _img_Hy.data;

}

bool CDenseSingleDimensionIntegralImage::TranslateCoordinate(
	int& new_ix, int& new_iy,
	double& new_fx, double& new_fy,
	double old_x, double old_y) const
{
	bool ret = (old_x>-EPS && old_y>-EPS && old_x<W+EPS && old_y<H+EPS);
	old_x += 0.5; old_y += 0.5;
	new_ix = CMyMath::TranslateNumber_Floor(old_x);
	new_iy = CMyMath::TranslateNumber_Floor(old_y);
	new_ix = (new_ix<0 ? 0 : (new_ix>W ? W : new_ix));
	new_iy = (new_iy<0 ? 0 : (new_iy>H ? H : new_iy));
	new_fx = old_x - new_ix;
	new_fy = old_y - new_iy;
	return ret;
}

double CDenseSingleDimensionIntegralImage::GetPixel(double x, double y) const
{
	int ix, iy;
	double fx, fy;
	bool fIsIn = TranslateCoordinate(ix, iy, fx, fy, x, y);
	if (!fIsIn) return 0.0;
	const double* qd = _imgEnlarged.data + ix + iy * _W2;
	return CMyMath::DoInterpolation_Bilinear(qd[0], qd[_W2], qd[1], qd[1+_W2], fx, fy);
}

double CDenseSingleDimensionIntegralImage::GetIntegral(double x, double y) const
{
	int ix, iy;
	double fx, fy;
	bool fIsIn = TranslateCoordinate(ix, iy, fx, fy, x, y);
	fx *= 0.50; fy *= 0.50;
	size_t offset_W1 = ix + iy * _W1;
	size_t offset_W2 = ix + iy * _W2;
	const double* qd = _imgEnlarged.data + offset_W2;
	double ret = 0.0
		+ fx * ((1.00 - fx) * _base_Vx[offset_W2] + fx * _base_Vx[offset_W2 +   1])
		+ fy * ((1.00 - fy) * _base_Hy[offset_W1] + fy * _base_Hy[offset_W1 + _W1])
		+ 0.25 * _base_x0y0[offset_W1]
		+ 4.00 * fx * fy * (0.0
			+ qd[0]
			+ (qd[1] - qd[0]) * fx
			+ (qd[_W2] - qd[0]) * fy
			+ (qd[0] - qd[1] - qd[_W2] + qd[1 + _W2]) * fx * fy);
	return ret;
}

double CDenseSingleDimensionIntegralImage::GetAverage(double center_x, double center_y, double width, double height) const
{
	if (width < EPS) width = EPS;
	if (height < EPS) height = EPS;
	width *= 0.5;
	height *= 0.5;
	double xa = center_x - width; xa = (xa<0 ? 0 : (xa>W ? W : xa));
	double xb = center_x + width; xb = (xb<0 ? 0 : (xb>W ? W : xb));
	double ya = center_y - height; ya = (ya<0 ? 0 : (ya>H ? H : ya));
	double yb = center_y + height; yb = (yb<0 ? 0 : (yb>H ? H : yb));
	double area = (xb - xa) * (yb - ya); area = (area<EPS ? EPS : area);
	double ret = GetIntegral(xa, ya) - GetIntegral(xb, ya) - GetIntegral(xa, yb) + GetIntegral(xb, yb);
	return ret / area;
}

//void CDenseSingleDimensionIntegralImage::myInit(string strFN_SourceImage)
//{
//	CImg<int> imgIntRGB;
//	CExternalTools::myLoadImage(strFN_SourceImage, imgIntRGB);
//	myInit(imgIntRGB);
//}

// [*] C_ImgTran

C_ImgTran::~C_ImgTran()
{
}

C_ImgTran* C_ImgTran::myClone() const
{
	return new C_ImgTran();
}

void C_ImgTran::ToInverseTransformation()
{
}

void C_ImgTran::Work(double& NewX, double& NewY, double OldX, double OldY) const
{
	NewX = OldX;
	NewY = OldY;
}

void C_ImgTran::Inverse(double& NewX, double& NewY, double OldX, double OldY) const
{
	NewX = OldX;
	NewY = OldY;
}

// [*] CImageTransformation

CImageTransformation::CImageTransformation()
{
	TranList.Clear();
	Clear();
}

void CImageTransformation::CloneDataFrom(const CImageTransformation& AnotherInstance)
{
	Clear();
	int i, ii;
	ii = (int)AnotherInstance.TranList.Size;
	const C_ImgTran* const* ppit = AnotherInstance.TranList.pElement;
	for ( i = 0; i < ii; ++i, ++ppit )
		this->PushBack((*ppit)->myClone());
}

CImageTransformation::CImageTransformation(const CImageTransformation& AnotherInstance)
{
	TranList.Clear();
	CloneDataFrom(AnotherInstance);
}

CImageTransformation& CImageTransformation::operator = (const CImageTransformation& AnotherInstance)
{
	if (&AnotherInstance != this)
		CloneDataFrom(AnotherInstance);
	return *this;
}

void CImageTransformation::Clear()
{
	_Type = TYPE_COMPOSITE;
	CDataStructureTools::ReleasePointers(TranList);
}

void CImageTransformation::PushBack(C_ImgTran* pNewTransformation)
{
	TranList.PushBack(pNewTransformation);
}

CImageTransformation::~CImageTransformation()
{
	CDataStructureTools::ReleasePointers(TranList);
}

C_ImgTran* CImageTransformation::myClone() const
{
	CImageTransformation* ret = new CImageTransformation();
	int i;
	const C_ImgTran* const* ppit = TranList.pElement;
	for ( i = 0; i < (int)TranList.Size; ++i, ++ppit )
		ret->PushBack((*ppit)->myClone());
	return ret;
}

void CImageTransformation::ToInverseTransformation()
{
	int i, ii;
	C_ImgTran** ppit;
	C_ImgTran* qit;
	ii = (int)TranList.Size;
	ppit = TranList.pElement;
	for ( i = 0; i < ii; ++i )
		(ppit[i])->ToInverseTransformation();
	ppit = TranList.pElement;
	for ( i = 0; i+i+1 < ii; ++i )
	{
		qit = ppit[i];
		ppit[i] = ppit[ii-1-i];
		ppit[ii-1-i] = qit;
	}
}

void CImageTransformation::Work(double& NewX, double& NewY, double OldX, double OldY) const
{
	NewX = OldX; NewY = OldY;

	int i, ii;
	ii = (int)TranList.Size;
	const C_ImgTran* const* ppit = TranList.pElement;
	for ( i = 0; i < ii; ++i, ++ppit )
	{
		OldX = NewX; OldY = NewY;
		(*ppit)->Work(NewX, NewY, OldX, OldY);
	}
}

void CImageTransformation::Inverse(double& OldX, double& OldY, double NewX, double NewY) const
{
	OldX = NewX; OldY = NewY;

	int i, ii;
	ii = (int)TranList.Size;
	const C_ImgTran* const* ppit = TranList.pElement + TranList.Size - 1;
	for ( i = ii-1; i >= 0; --i, --ppit )
	{
		NewX = OldX; NewY = OldY;
		(*ppit)->Inverse(OldX, OldY, NewX, NewY);
	}
}

// [*] CTransformableImage

// [*] C_ImgTran_Translate

C_ImgTran_Translate::C_ImgTran_Translate()
{
	myInit(0.0, 0.0);
}

C_ImgTran_Translate::C_ImgTran_Translate(double dx, double dy)
{
	myInit(dx, dy);
}

C_ImgTran_Translate::C_ImgTran_Translate(double OldX, double OldY, double NewX, double NewY)
{
	myInit(OldX, OldY, NewX, NewY);
}

void C_ImgTran_Translate::myInit(double dx, double dy)
{
	_Type = TYPE_TRANSLATE;
	this->dx = dx;
	this->dy = dy;
}

void C_ImgTran_Translate::myInit(double OldX, double OldY, double NewX, double NewY)
{
	myInit(NewX - OldX, NewY - OldY);
}

C_ImgTran_Translate::~C_ImgTran_Translate()
{
}

C_ImgTran* C_ImgTran_Translate::myClone() const
{
	C_ImgTran* ret = new C_ImgTran_Translate(dx, dy);
	return ret;
}

void C_ImgTran_Translate::ToInverseTransformation()
{
	myInit(-dx, -dy);
}

void C_ImgTran_Translate::Work(double& NewX, double& NewY, double OldX, double OldY) const
{
	NewX = OldX + dx;
	NewY = OldY + dy;
}

void C_ImgTran_Translate::Inverse(double& OldX, double& OldY, double NewX, double NewY) const
{
	OldX = NewX - dx;
	OldY = NewY - dy;
}

// [*] C_ImgTran_Rotate

C_ImgTran_Rotate::C_ImgTran_Rotate()
{
	myInit(0.0, 0.0, 0.0);
}

C_ImgTran_Rotate::C_ImgTran_Rotate(double CenterX, double CenterY, double Rotate)
{
	myInit(CenterX, CenterY, Rotate);
}

void C_ImgTran_Rotate::myInit(double CenterX, double CenterY, double Rotate)
{
	_Type = TYPE_ROTATE;
	cx = CenterX;
	cy = CenterY;
	rot = Rotate;
	cos_r = cos(rot);
	sin_r = sin(rot);
}

C_ImgTran_Rotate::~C_ImgTran_Rotate()
{
}

C_ImgTran* C_ImgTran_Rotate::myClone() const
{
	C_ImgTran* ret = new C_ImgTran_Rotate(cx, cy, rot);
	return ret;
}

void C_ImgTran_Rotate::ToInverseTransformation()
{
	myInit(cx, cy, -rot);
}

void C_ImgTran_Rotate::Work(double& NewX, double& NewY, double OldX, double OldY) const
{
	OldX -= cx;
	OldY -= cy;
	// NOTICE!! THE COORDINATE ARE INVERSED ON Y-AXIS!!
	NewX = cx + OldX * cos_r + OldY * sin_r;
	NewY = cy + OldY * cos_r - OldX * sin_r;
}

void C_ImgTran_Rotate::Inverse(double& OldX, double& OldY, double NewX, double NewY) const
{
	NewX -= cx;
	NewY -= cy;
	// NOTICE!! THE COORDINATE ARE INVERSED ON Y-AXIS!!
	OldX = cx + NewX * cos_r - NewY * sin_r;
	OldY = cy + NewY * cos_r + NewX * sin_r;
}

// [*] C_ImgTran_Scale

C_ImgTran_Scale::C_ImgTran_Scale()
{
	myInit(0.0, 0.0, 1.0, 1.0);
}

//C_ImgTran_Scale::C_ImgTran_Scale(double Scale)
//{
//	myInit(Scale);
//}
//
//C_ImgTran_Scale::C_ImgTran_Scale(double ScaleX, double ScaleY)
//{
//	myInit(ScaleX, ScaleY);
//}
//
//void C_ImgTran_Scale::myInit(double Scale)
//{
//	myInit(Scale, Scale);
//}
//
//void C_ImgTran_Scale::myInit(double ScaleX, double ScaleY)
//{
//	//TODO
//}

C_ImgTran_Scale::C_ImgTran_Scale(double CenterX, double CenterY, double Scale)
{
	myInit(CenterX, CenterY, Scale);
}

C_ImgTran_Scale::C_ImgTran_Scale(double CenterX, double CenterY, double ScaleX, double ScaleY)
{
	myInit(CenterX, CenterY, ScaleX, ScaleY);
}

void C_ImgTran_Scale::myInit(double CenterX, double CenterY, double Scale)
{
	myInit(CenterX, CenterY, Scale, Scale);
}

void C_ImgTran_Scale::myInit(double CenterX, double CenterY, double ScaleX, double ScaleY)
{
	_Type = TYPE_SCALE;
	cx = CenterX;
	cy = CenterY;
	sx = ScaleX; if (sx>-EPS && sx<EPS) sx = 1.0;
	sy = ScaleY; if (sy>-EPS && sy<EPS) sy = 1.0;
}

C_ImgTran_Scale::~C_ImgTran_Scale()
{
}

C_ImgTran* C_ImgTran_Scale::myClone() const
{
	C_ImgTran* ret = new C_ImgTran_Scale(cx, cy, sx, sy);
	return ret;
}

void C_ImgTran_Scale::ToInverseTransformation()
{
	myInit(cx, cy, 1/sx, 1/sy);
}

void C_ImgTran_Scale::Work(double& NewX, double& NewY, double OldX, double OldY) const
{
	NewX = cx + (OldX - cx) * sx;
	NewY = cy + (OldY - cy) * sy;
}

void C_ImgTran_Scale::Inverse(double& OldX, double& OldY, double NewX, double NewY) const
{
	OldX = cx + (NewX - cx) / sx;
	OldY = cy + (NewY - cy) / sy;
}

